#include "configureinfo.h"
#include <algorithm>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fstream>
#include <inttypes.h>
#include <iostream>
#include <sstream>

ConfigureInfo::ConfigureInfo()
: m_autoCheckInter(60), m_bStopCheck(false), m_bStartCheck(false)
{
}

ConfigureInfo::~ConfigureInfo()
{
    if (m_bStartCheck) {
        m_bStopCheck = true;
        pthread_join(m_checkThreadId, NULL);
    }
    for (std::map<std::string, pthread_mutex_t *>::iterator it = m_fileMutexes.begin();
         it != m_fileMutexes.end(); ++it) {
        pthread_mutex_destroy(it->second);
        delete it->second;
        it->second = NULL;
    }
}

bool ConfigureInfo::ReadConfigue(const char *dirname, bool bChild, std::string &err)
{
    bool bRet = true;
    DIR *dir = NULL;
    // *dir1;
    struct dirent *ptr = NULL;
    dir = opendir(dirname);
    if (NULL == dir) {
        err.append("open dir ").append(dirname).append("error :").append(strerror(errno));
        bRet = false;
    } else {
        while ((ptr = readdir(dir)) != NULL) {

            if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) {
                continue;
            } else {
                std::string filename = ptr->d_name;
                std::string curFile = dirname;
                if (curFile.size() - 1 != curFile.find_last_of('/')) {
                    curFile += "/";
                }
                curFile += filename;
                if (DT_REG == ptr->d_type) {
                    if (!bConfigFile(ptr->d_name)) {
                        continue;
                    } else {
                        if (!ReadConfigue(curFile.c_str(), err)) {
                            bRet = false;
                            break;
                        }
                        /*ReadFileInfo(curFile.c_str(), ptr->d_name);
                        if(m_fileMutexes.end() == m_fileMutexes.find(ptr->d_name))
                        {
                            pthread_mutex_t * mutex = new pthread_mutex_t;
                            pthread_mutex_init(mutex,NULL);
                            m_fileMutexes[ptr->d_name] = mutex;
                        }*/
                    }
                } else if (bChild && (DT_DIR == ptr->d_type)) {
                    if (!ReadConfigue(curFile.c_str(), bChild, err)) {
                        bRet = false;
                        break;
                    }
                } else {
                    continue;
                }
            }
        }
        closedir(dir);
    }
    return bRet;
}

bool ConfigureInfo::ReadConfigue(const char *configFile, std::string &err)
{
    std::string fileName = GetFileName(configFile);
    if (fileName.empty() || !bConfigFile(fileName.c_str())) {
        err.append("file name is empty or file is not of .conf file");
        return false;
    }
    ReadFileInfo(configFile, fileName.c_str());
    if (m_fileMutexes.end() == m_fileMutexes.find(fileName)) {
        pthread_mutex_t *mutex = new pthread_mutex_t;
        pthread_mutex_init(mutex, NULL);
        m_fileMutexes[fileName] = mutex;
    }
    return true;
}

bool ConfigureInfo::ReloadOne(const char *filepath)
{
    if ((-1 == access(filepath, F_OK)) || (-1 == access(filepath, R_OK))) {
        return false;
    } else {
        std::string file = filepath;
        size_t pos = file.rfind('/');
        if (pos == std::string::npos) {
            return false;
        } else {
            std::string filename = file.substr(pos + 1);
            if (bConfigFile(filename.c_str())) {
                ReadFileInfo(filepath, filename.c_str());
            } else {
                return false;
            }
        }
    }
    return true;
}

std::string ConfigureInfo::GetFileName(const char *filepath)
{
    std::string strName("");
    if ((-1 == access(filepath, F_OK)) || (-1 == access(filepath, R_OK))) {
        return strName;
    } else {
        strName = filepath;
        size_t pos = strName.rfind('/');
        if (pos == std::string::npos) {
            return strName;
        } else {
            return strName.substr(pos + 1);
        }
    }
}

void ConfigureInfo::InsertValueToList(const char *modulename, const std::string &key, const std::string &value)
{
    std::map<std::string, std::string>::iterator it = m_conf.find(key);
    std::map<std::string, pthread_mutex_t *>::iterator itMutex = m_fileMutexes.find(modulename);
    if (it == m_conf.end()) {
        if (itMutex != m_fileMutexes.end()) {
            pthread_mutex_lock(itMutex->second);
            m_conf[key] = value;
            pthread_mutex_unlock(itMutex->second);
        } else {
            m_conf[key] = value;
        }
    } else {
        if (0 != it->second.compare(value)) {
            if (itMutex != m_fileMutexes.end()) {
                pthread_mutex_lock(itMutex->second);
                m_conf[key] = value;
                pthread_mutex_unlock(itMutex->second);
            } else {
                m_conf[key] = value;
            }
        }
    }
}

void ConfigureInfo::ReadFileInfo(const char *filename, const char *modulename)
{
    std::ifstream ifs(filename, std::ifstream::in);
    std::string linevalue;
    std::string linekey;
    bool blineend = true;
    std::string strLine;
    while (getline(ifs, strLine)) {
        TrimLine(strLine);
        if (strLine.empty() || strLine.at(0) == '#') {
            continue;
        } else {
            size_t iLen = strLine.size();
            if (!blineend) {
                if (strLine.at(iLen - 1) == '\\') {
                    linevalue += strLine.erase(iLen - 1, 1);
                } else {
                    linevalue += strLine;
                    blineend = true;
                    InsertValueToList(modulename, linekey, linevalue);
                }
            } else {
                size_t pos = strLine.find_first_of("=");
                if (pos == std::string::npos) {
                    continue;
                } else {
                    if (pos > 0 && (pos + 1) < iLen) {
                        std::string key = strLine.substr(0, pos);
                        std::string value = strLine.substr(pos + 1, iLen - pos - 1);
                        TrimLine(key);
                        TrimLine(value);
                        linekey = GetKeyName(modulename, key.c_str());
                        if (value.at(value.size() - 1) == '\\') {
                            linevalue = value.erase(value.size() - 1, 1);
                            blineend = false;
                        } else {
                            InsertValueToList(modulename, linekey, value);
                        }
                    } else {
                        continue;
                    }
                }
            }
        }
    }
    ifs.close();
}

bool ConfigureInfo::bConfigFile(const char *filename)
{
    const char *ptr = strrchr(filename, '.');
    if (ptr && (strcmp(ptr, ".conf") == 0)) {
        return true;
    } else {
        return false;
    }
}

void ConfigureInfo::TrimLine(std::string &strLine)
{
    //方法一，使用stl的算法解决
    strLine.erase(strLine.begin(), std::find_if(strLine.begin(), strLine.end(),
                                                std::not1(std::ptr_fun(::isspace))));
    strLine.erase(
    std::find_if(strLine.rbegin(), strLine.rend(), std::not1(std::ptr_fun(::isspace))).base(),
    strLine.end());
    return;
    //方法二、使用纯c方式
    if (strLine.empty()) {
        return;
    }
    const char *str = strLine.c_str();
    int i = 0;
    int iLastPos = strLine.size() - 1;
    int iLeft = 0;
    int iRight = iLastPos;
    while (str[i]) {
        iLeft = i;
        if (0 == isspace(str[i])) {
            break;
        }
        i++;
    }
    if (iLeft == iLastPos) {
        strLine.clear();
        return;
    }
    while (iLastPos >= 0) {
        iRight = iLastPos;
        if (0 == isspace(str[iLastPos])) {
            break;
        }
        iLastPos--;
    }
    strLine = strLine.substr(iLeft, iRight - iLeft + 1);
}

std::string ConfigureInfo::GetKeyName(const char *modulename, const char *key)
{
    std::string keyname = modulename;
    keyname.append(".").append(key);
    return keyname;
}

void ConfigureInfo::AddAutoReloadFile(const std::string &filePath)
{
    if (!filePath.empty() && !GetFileName(filePath.c_str()).empty()) {
        m_autoReloadModul.push_back(filePath);
    }
}

std::vector<std::string> &ConfigureInfo::GetAutoReloadFiles()
{
    return m_autoReloadModul;
}

void ConfigureInfo::StartAutoReload(const int &checkInter)
{
    if (checkInter < 1) {
        return;
    }

    m_autoCheckInter = checkInter;
    if (!m_bStartCheck) {
        m_bStartCheck =
        pthread_create(&m_checkThreadId, NULL, ConfigureInfo::AutoReladThread, this) == 0 ? true : false;
    }
}

int ConfigureInfo::GetAutoReloadInter()
{
    if (m_autoCheckInter < 1) {
        return 60;
    } else {
        return m_autoCheckInter;
    }
}

bool ConfigureInfo::GetStopCheckState()
{
    return m_bStopCheck;
}

void *ConfigureInfo::AutoReladThread(void *para)
{
    ConfigureInfo *configInfo = (ConfigureInfo *)para;
    while (1) {
        if (configInfo->GetStopCheckState()) {
            break;
        }
        sleep(configInfo->GetAutoReloadInter());
        std::vector<std::string> modules = configInfo->GetAutoReloadFiles();
        for (size_t i = 0; i < modules.size(); ++i) {
            configInfo->ReloadOne(modules[i].c_str());
        }
    }
    return NULL;
}

bool ConfigureInfo::SetDefaultValue(const char *modleName, const char *key, const char *defValue)
{
    if (NULL == modleName || NULL == key) {
        return false;
    }
    std::string strKey = GetKeyName(modleName, key);
    m_defStrValue[strKey] = defValue;
    return true;
}
bool ConfigureInfo::SetDefaultValue(const char *modleName, const char *key, const int &defValue)
{
    if (NULL == modleName || NULL == key) {
        return false;
    }
    char tmpValue[33] = { 0 };
    sprintf(tmpValue, "%d", defValue);
    return SetDefaultValue(modleName, key, tmpValue);
}

bool ConfigureInfo::SetDefaultValue(const char *modleName, const char *key, const long long &defValue)
{
    if (NULL == modleName || NULL == key) {
        return false;
    }
    char tmpValue[65] = { 0 };
    sprintf(tmpValue, "%lld", defValue);
    return SetDefaultValue(modleName, key, tmpValue);
}

bool ConfigureInfo::SetDefaultValue(const char *modleName, const char *key, const double &defValue)
{
    if (NULL == modleName || NULL == key) {
        return false;
    }
    char tmpValue[129] = { 0 };
    sprintf(tmpValue, "%f", defValue);
    return SetDefaultValue(modleName, key, tmpValue);
}

std::string ConfigureInfo::GetStrValue(const char *filename, const char *key)
{
    std::map<std::string, std::string>::iterator it = m_conf.find(GetKeyName(filename, key));
    std::map<std::string, pthread_mutex_t *>::iterator itMutex = m_fileMutexes.find(filename);
    std::string strReturn = "";
    if (itMutex != m_fileMutexes.end()) {
        pthread_mutex_lock(itMutex->second);
    }
    if (it != m_conf.end()) {
        strReturn = it->second;
    } else {
        it = m_defStrValue.find(GetKeyName(filename, key));
        if (it != m_defStrValue.end()) {
            strReturn = it->second;
        }
    }
    if (itMutex != m_fileMutexes.end()) {
        pthread_mutex_unlock(itMutex->second);
    }
    return strReturn;
}

int ConfigureInfo::GetIntValue(const char *modulename, const char *key)
{
    std::string strValue = GetStrValue(modulename, key);
    if (strValue.empty()) {
        return -65535;
    } else {
        return strtoimax(strValue.c_str(), NULL, 10);
    }
}

double ConfigureInfo::GetDoubleValue(const char *modulename, const char *key)
{
    // double dValue=0.0;
    std::string strValue = GetStrValue(modulename, key);
    if (strValue.empty()) {
        return 0.0f;
    } else {
        return strtod(strValue.c_str(), NULL);
    }
}

long long ConfigureInfo::GetLongValue(const char *modulename, const char *key)
{
    std::string strValue = GetStrValue(modulename, key);
    if (strValue.empty()) {
        return 0;
    } else {
        return strtoll(strValue.c_str(), NULL, 10);
    }
}

bool ConfigureInfo::GetBoolValue(const char *modulename, const char *key)
{
    int iNum = GetIntValue(modulename, key);
    return 1 == iNum ? true : false;
}
